﻿using System;
using System.Collections.Generic;
using System.Threading;
using PmfWeb.Core.Cache;

namespace PmfWeb.Core.Web
{
    ///<summary>
    /// 用户在线统计系统
    ///</summary>
    public class Online<Key,value> where Key: IComparable<Key> where value : OnlineUser<Key>, new()
    {
        #region 成员变量
        private TimerCallback callback = new TimerCallback(RemoveTimeOutUser);

        /// <summary>
        /// 会员在线信息列表
        /// </summary>
        private static Dictionary<string, LinkedListNode<value>> memberList;
        /// <summary>
        /// 所有在线用户信息列表
        /// </summary>
        private static Dictionary<Key, LinkedListNode<value>> allList;
        /// <summary>
        /// 会员排序列表
        /// </summary>
        private static LinkedList<value> valueList;
        /// <summary>
        /// 缓存锁
        /// </summary>
        private static ReaderWriterLock locker = new ReaderWriterLock();

        /// <summary>
        /// 更新在线列表计数器
        /// </summary>
        //private System.Timers.Timer updateTimer;
        private System.Threading.Timer updateTimer;
        /// <summary>
        /// 登陆超时时间
        /// </summary>
        private double timerOut;
        /// <summary>
        /// 对象
        /// </summary>
        public static Online<Key, value> iOnline = null;
        #endregion

        #region 创建实例 public static Online<TSKey, value> Create(int iTimeOunt)
        /// <summary>
        ///  创建实例
        /// </summary>
        /// <param name="iTimeOunt"></param>
        /// <returns></returns>
        public static Online<Key, value> Create(int iTimeOunt)
        {
            if(null == iOnline) iOnline = new Online<Key, value>(iTimeOunt);
            return iOnline;
        }
        #endregion

        #region 构造器
        ///<summary>
        /// 构造器
        ///</summary>
        ///<param name="iTimeOut">执行频率(或周期)</param>
        private Online(int iTimeOut)
        {
            memberList = new Dictionary<string, LinkedListNode<value>>();
            allList = new Dictionary<Key, LinkedListNode<value>>();
            valueList = new LinkedList<value>();
            timerOut = 1000 * Math.Abs(iTimeOut);
            AutoResetEvent reset = new AutoResetEvent(false);
            updateTimer = new System.Threading.Timer(callback, reset, 1000 * 10 *60, 1000*3*60);
            //reset.WaitOne(30*1000, false);  //等待任务执行完成(等待30秒)
            //updateTimer.AutoReset = true;
            //updateTimer.Elapsed += RemoveTimeOutUser;
            //updateTimer.Interval = timerOut;   //定时执行
            //updateTimer.Start();
        }
        #endregion

        #region 清除超时用户信息 private void RemoveTimeOutUser(object sender, ElapsedEventArgs e)
        /// <summary>
        /// 清除超时用户信息
        /// </summary>
        private static void RemoveTimeOutUser(object sender)
        {
            //double NextRunTime = timerOut;
            //DateTime _CheckTime = e.SignalTime.AddMilliseconds(timerOut * -1);
            //updateTimer.Stop();
            try
            {
                locker.AcquireWriterLock(Timeout.Infinite);
                if (valueList.Count > 0)
                {
                    LinkedListNode<value> UserNode = valueList.Last;
                    TimeSpan ts = UserNode.Value.End - DateTime.Now;
                    if (ts.TotalMilliseconds <= 0)
                    {
                        if (UserNode.Previous != null)
                        {
                            //ts = UserNode.Previous.Value.End - _CheckTime;
                            //if (ts.TotalMilliseconds <= 0)
                            //    NextRunTime = 1;
                            //else
                            //    NextRunTime = ts.TotalMilliseconds;
                        }
                        Remove(UserNode);
                    }
                    else
                    {
                       // NextRunTime = ts.TotalMilliseconds;
                    }
                }
                //updateTimer.Interval = NextRunTime;
            }
            finally
            {
                locker.ReleaseWriterLock();
            }
            //updateTimer.Start();
        }
        #endregion

        #region 移除用户资料 private void Remove(LinkedListNode<value> UserNode)
        /// <summary>
        /// 移除用户资料
        /// </summary>
        /// <param name="UserNode"></param>
        public static void Remove(LinkedListNode<value> UserNode)
        {
            if (null != UserNode.List)
            {
                allList.Remove(UserNode.Value.Uid);
                if (1 == UserNode.Value.UType)
                    memberList.Remove(UserNode.Value.Name);
                UserNode.List.Remove(UserNode);
            }
        }
        #endregion

        #region 实现枚举器 public IEnumerable<value> GetOnlineEnumertor
        /// <summary>
        /// 获取枚举器
        /// </summary>
        public IEnumerable<value> GetOnlineEnumertor
        {
            get
            {
                locker.AcquireReaderLock(Timeout.Infinite);
                foreach (value value1 in valueList)
                {
                    yield return value1;
                }
                locker.ReleaseReaderLock();
            }
        }
        #endregion

        #region 获取在线列表 public List<value> GetOnlineList
        ///<summary>
        /// 获取在线列表
        ///</summary>
        public List<value> GetOnlineList
        {
            get
            {
                locker.AcquireReaderLock(Timeout.Infinite);
                List<value> onlines = new List<value>();
                onlines.AddRange(valueList);
                locker.ReleaseReaderLock();
                return onlines;
            }
        }
        #endregion

        #region 根据用户名获取用户信息 public value GetUserByName(string name)
        /// <summary> 
        /// 根据用户名获取用户信息
        /// </summary>
        /// <param name="name">用户名</param>
        /// <returns>用户实体</returns>
        public value GetUserByName(string name)
        {
            value uValue = default(value);
            LinkedListNode<value> user;
            try
            {
                locker.AcquireReaderLock(Timeout.Infinite);
                if(memberList.TryGetValue(name,out user))
                {
                    uValue = user.Value;
                }
            }
            catch (Exception)
            {
            }
            finally
            {
                locker.ReleaseReaderLock();
            }
            return uValue;
        }
        #endregion

        #region 添加新用户 public void AddUser(TSKey key, value uValue)
        /// <summary>
        /// 添加新用户
        /// </summary>
        /// <param name="key">用户Key</param>
        /// <param name="uValue">用户信息实体</param>
        public void AddUser(Key key, value uValue)
        {
            LinkedListNode<value> oldUser;
            uValue.Uid = key;

            LinkedListNode<value> onLineUser = new LinkedListNode<value>(uValue);
            try
            {
                locker.AcquireWriterLock(Timeout.Infinite);
                if(allList.TryGetValue(key,out oldUser))
                {
                    oldUser.Value = onLineUser.Value;
                    ReNewUser(oldUser);
                }
                else
                {
                    if(1 == uValue.UType)
                    {
                        if(memberList.TryGetValue(uValue.Name,out oldUser))
                        {
                            memberList.Add(uValue.Name, onLineUser);
                            allList.Add(key, onLineUser);
                            ReNewUser(onLineUser);
                        }
                        else
                        {
                            oldUser.Value = onLineUser.Value;
                            ReNewUser(oldUser);
                        }
                    }
                    else
                    {
                        allList.Add(key, onLineUser);
                        ReNewUser(onLineUser);
                    }
                }

            }
            catch (Exception)
            {
            }
            finally
            {
                locker.ReleaseWriterLock();
            }

        }
        #endregion

        #region 更新在线用户信息 public void Update(TSKey iKey)
        /// <summary>
        /// 更新在线用户信息
        /// </summary>
        /// <param name="iKey">用户类别Key</param>
        public void Update(Key iKey)
        {
            LinkedListNode<value> user;
            try
            {
                locker.AcquireWriterLock(Timeout.Infinite);
                if(allList.TryGetValue(iKey,out user))
                {
                    ReNewUser(user);
                }

            }
            finally
            {
                locker.ReleaseWriterLock();
            }
        }
        #endregion

        #region 更新用户最后访问地址 public void Update(TSKey iKey, string lastUrl)
        /// <summary>
        /// 更新用户最后访问地址
        /// </summary>
        /// <param name="iKey">关键字</param>
        /// <param name="lastUrl">最后访问地址</param>
        public void Update(Key iKey, string lastUrl)
        {
            LinkedListNode<value> user;
            try
            {
                locker.AcquireWriterLock(Timeout.Infinite);
                if(allList.TryGetValue(iKey,out user))
                {
                    ReNewUser(user, lastUrl);
                }
            }
            finally
            {
                locker.ReleaseWriterLock();
            }
        }
        #endregion

        #region 重建并更新链表关系 private bool ReNewUser(LinkedListNode<value> iValue)
        /// <summary>
        /// 重建并更新链表关系
        /// </summary>
        /// <param name="iValue">用户实体</param>
        /// <returns>是否重建成功</returns>
        private bool ReNewUser(LinkedListNode<value> iValue)
        {
            if(null != iValue.List)
            {
                iValue.List.Remove(iValue);
            }
            valueList.AddFirst(iValue);
            TimeSpan span = DateTime.Now - iValue.Value.Start;
            iValue.Value.OnlineMinutes = span.TotalMinutes;
            iValue.Value.End = DateTime.Now;
            return true;
        }
        #endregion

        #region 重建并更新链表关系 private bool ReNewUser(LinkedListNode<value> iValue, string lastUrl)
        /// <summary>
        /// 重建并更新链表关系
        /// </summary>
        /// <param name="iValue">链表对象</param>
        /// <param name="lastUrl">最后访问地址</param>
        /// <returns>是否重建并更新数据成功</returns>
        private bool ReNewUser(LinkedListNode<value> iValue, string lastUrl)
        {
            if(null != iValue.List)
            {
                iValue.List.Remove(iValue);
            }
            valueList.AddFirst(iValue);
            TimeSpan span = DateTime.Now - iValue.Value.Start;
            iValue.Value.OnlineMinutes = span.TotalMinutes;
            iValue.Value.LastUrl = lastUrl;
            iValue.Value.End = DateTime.Now;
            return true;
        }
        #endregion

        #region 移除用户 public void Remove(TSKey iKey)
        /// <summary>
        /// 移除用户
        /// </summary>
        /// <param name="iKey">key</param>
        public void Remove(Key iKey)
        {
            LinkedListNode<value> user;
            try
            {
                locker.AcquireWriterLock(Timeout.Infinite);
                if(allList.TryGetValue(iKey, out user))
                {
                    Remove(user);
                }
            }
            finally
            {
                locker.ReleaseWriterLock();
            }
        }
        #endregion

        #region 从在线列表里删除指定的用户 public void Remove(string name)
        /// <summary>
        /// 从在线列表里删除指定的用户
        /// </summary>
        /// <param name="name">用户名</param>
        public void RemoveByName(string name)
        {
            LinkedListNode<value> user;
            try
            {
                locker.AcquireWriterLock(Timeout.Infinite);
                if (memberList.TryGetValue(name, out user))
                {
                    Remove(user);
                }
            }
            finally
            {
                locker.ReleaseWriterLock();
            }
        }
        #endregion

        #region 判断用户是否在线 public bool IsOnline(TSKey iKey)
        /// <summary>
        /// 判断用户是否在线
        /// </summary>
        /// <param name="iKey">key</param>
        /// <returns>在线,返回true;反之返回false</returns>
        public bool IsOnline(Key iKey)
        {
            bool result = false;
            try
            {
                locker.AcquireReaderLock(Timeout.Infinite);
                if(allList.ContainsKey(iKey)) result = true;
            }
            catch (Exception)
            {
            }
            finally
            {
                locker.ReleaseReaderLock();
            }
            return result;
        }
        #endregion

        #region 判断会员是否在线 public bool IsOnline(string name)
        /// <summary>
        /// 判断会员是否在线
        /// </summary>
        /// <param name="name">会员用户名</param>
        /// <returns>在线返回true,反之返回false</returns>
        public bool IsOnline(string name)
        {
            bool result = false;
            try
            {
                locker.AcquireReaderLock(Timeout.Infinite);
                if(memberList.ContainsKey(name)) result = true;
            }
            catch (Exception)
            {
            }
            finally
            {
                locker.ReleaseReaderLock();
            }
            return result;
        }
        #endregion

        #region 所有在线用户数 public int AllCount
        /// <summary>
        /// 所有在线用户数
        /// </summary>
        public int AllCount
        {
            get
            {
                return allList.Count;
            }
        }
        #endregion

        #region 所有在线会员数
        /// <summary>
        /// 所有会员在线数
        /// </summary>
        public int MemberCount
        {
            get
            {
                return memberList.Count;
            }
        }
        #endregion

        #region 清除所有用户 public void Clear()
        /// <summary>
        /// 清除所有用户
        /// </summary>
        public void Clear()
        {
            locker.AcquireWriterLock(Timeout.Infinite);
            allList.Clear();
            memberList.Clear();
            valueList.Clear();
            locker.ReleaseWriterLock();
        }
        #endregion
    }

    #region 在线用户实体
    ///<summary>
    /// 在线用户实体
    ///</summary>
    ///<typeparam name="TSKey"></typeparam>
    public class OnlineUser<TSKey>
    {
        /// <summary>
        /// 用户标识
        /// </summary>
        private TSKey _uid;
        /// <summary>
        /// 用户名
        /// </summary>
        private string _name = "游客";
        /// <summary>
        /// 开始访问时间
        /// </summary>
        private DateTime _start = DateTime.Now;
        /// <summary>
        /// 结束访问时间
        /// </summary>
        private DateTime _end = DateTime.Now;
        /// <summary>
        /// 最后访问IP
        /// </summary>
        private string _ip = Current.GetIP();
        /// <summary>
        /// 在线时间
        /// </summary>
        private double _onlineMinutes;
        /// <summary>
        /// 用户类型
        /// </summary>
        private int _uType = 0;
        /// <summary>
        /// 最后访问网址
        /// </summary>
        private string _lastUrl = Current.GetPageName();
        /// <summary>
        /// 用户标识
        /// </summary>
        public TSKey Uid
        {
            get { return _uid; }
            set { _uid = value; }
        }
        /// <summary>
        /// 用户名(未登录用户为:游客)
        /// </summary>
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
        /// <summary>
        /// 开始访问时间
        /// </summary>
        public DateTime Start
        {
            get { return _start; }
            set { _start = value; }
        }
        /// <summary>
        /// 最后访问时间
        /// </summary>
        public DateTime End
        {
            get { return _end; }
            set { _end = value; }
        }
        /// <summary>
        /// 最后访问IP
        /// </summary>
        public string IP
        {
            get { return _ip; }
            set { _ip = value; }
        }
        /// <summary>
        /// 在线时长
        /// </summary>
        public double OnlineMinutes
        {
            get { return _onlineMinutes; }
            set { _onlineMinutes = value; }
        }
        /// <summary>
        /// 用户类型,0为游客,1为5173用户(登陆用户)
        /// </summary>
        public int UType
        {
            get { return _uType; }
            set { _uType = value; }
        }
        /// <summary>
        /// 最后访问地址
        /// </summary>
        public string LastUrl
        {
            get { return _lastUrl; }
            set { _lastUrl = value; }
        }
    }
    #endregion

}
